#!/usr/bin/env python2
# coding:utf-8

import os
import sys
import socket
import time
import subprocess
import fcntl
import types
import errno
import getopt
import random
import httplib
import urllib
import threading
#import pika

from wsgiref.simple_server import make_server

from daemon import Daemon
from config import Config
from utils import Exp, _hosts_load
from lich_check_suspend_admin import str2dict


class Lich_check_suspend_agent(Daemon):
    def __init__(self, config, admin_host=None, admin_port=None, agent_port=None):
        self.role = "lich_check_suspend_agent"
        self.admin_host = admin_host
        self.admin_port = int(admin_port)
        self.agent_port = int(agent_port)
        self.config = config
        self.hb_timeout = "/dev/shm/lich/hb_timeout/"
        self.hosts = []

        self.connection = None
        self.threads = []

        #self.hb_timeouts = {"idx": "pretime"}
        self.records = {}

        os.system('mkdir -p ' + self.hb_timeout)
        os.system('mkdir -p ' + self.config.home + '/tmp')
        os.system('mkdir -p ' + self.config.home + '/log')
        pidfile = '/var/run/%s.pid' % (self.role)
        log = self.config.home + '/log/%s.log' % (self.role)
        os.system('touch ' + log)
        super(Lich_check_suspend_agent, self).__init__(pidfile, '/dev/null', log, log, self.role)

    def list_clusterconf(self):
        hosts_version, hosts = _hosts_load(self.config.clusterconf)
        return hosts

    def set_netblock(self, netblock):
        d = "/dev/shm/lich/blocklist/"
        os.system("mkdir -p %s" % (d))

        blocks = os.listdir(d)
        if len(blocks) > 0:
            #一次只处理一个假死节点
            raise Exp(errno.EPERM, "has blocks, so raise")

        cmd = "cd %s;touch %s" % (d, netblock)
        os.system(cmd)
        print("set netblock %s, %s" % (d, netblock))

    def _load_hb_timeout(self, idx):
        lines = []
        with open(os.path.join(self.hb_timeout, idx), "r") as f:
            for x in  f.readlines():
                if x.strip():
                    lines.append(x.strip())
        return lines

    def _prep_check_hb_timeout(self):
        for idx in os.listdir(self.hb_timeout):
            timeouts = self._load_hb_timeout(idx)
            timeouts_new = []
        
            pretime = self.records.get(idx)
            if pretime is None:
                if timeouts:
                    nowtime = timeouts[-1].split(";")[0]
                    self.records.update({idx: nowtime})
            print("set pretime %s" % (self.records))

    def _check_hb_timeout(self, idx):
        timeouts = self._load_hb_timeout(idx)
        timeouts_new = []

        pretime = self.records.get(idx)
        if pretime is None:
            timeouts_new = timeouts[:]
            nowtime = timeouts[-1].split(";")[0]
            self.records.update({idx: nowtime})
            return timeouts_new

        valid = False
        for x in timeouts:
            if x.startswith(pretime):
                valid = True
                continue

            if valid:
                timeouts_new.append(x)
                nowtime = x.split(";")[0]
                self.records.update({idx: nowtime})

        return timeouts_new

    def check_hb_timeout(self):
        #sprintf(line, "%s; "NID_FORMAT"; %s; %s\n", time_buf, NID_ARG(&ng.local_nid), ng.name, lname);
        #[root@wu2 lich3]# cat /dev/shm/lich/hb_timeout/0 
        #2016-05-13 17:23:00; 1_v1462497384; wu2/0; wu5/0
        #2016-05-13 17:41:27; 1_v1462497384; wu2/0; wu5/0
        #2016-05-13 19:40:12; 1_v1462497384; wu2/0; wu3/1
        timeouts = []
        for idx in os.listdir(self.hb_timeout):
            t = self._check_hb_timeout(idx)
            if t:
                print("get hb timeout %s, self.records: %s" % (t, self.records))
            for x in t:
                src = x.split(";")[-2].strip()
                dist = x.split(";")[-1].strip()
                timeouts.append(";".join([src, dist]))
        return timeouts

    def _start_check_hb_timeout(self):
        # send x = src;dist
        # example: wu2/0;wu3/1

        self.hosts = list(set(self.list_clusterconf()))
        hosts = ';'.join(self.hosts)
        while True:
            hb_timeouts = self.check_hb_timeout()
            if hb_timeouts:
                #http send
                for x in hb_timeouts:
                    f = x.split(';')[0].strip()
                    t = x.split(';')[1].strip()
                    try:
                        params = urllib.urlencode({
                            'hb_timeout': x,
                            'hosts': hosts,
                        })
                        headers = {"Content-type": "application/x-www-form-urlencoded"}
                        conn = httplib.HTTPConnection(self.admin_host, self.admin_port)
                        conn.request("POST", '/hb_timeout?from='+f+'&to='+t+'&hosts='+hosts+'&port='+str(self.agent_port), params, headers)
                        response = conn.getresponse()
                        data = response.read()
                        conn.close()
                    except Exception, e:
                        print 'send hb_timeout exp:', e
                    finally:
                        if conn:
                            conn.close()
            time.sleep(1)

    def wait_finish(self):
        while True:
            fails = []
            for t in self.threads:
                if not t.is_alive():
                    fails.append(t)

            if fails:
                error = "%s was fail" % (",".join([x.getName() for x in fails]))
                raise Exception(error)

            time.sleep(1)

    def start_check_hb_timeout(self):
        self._prep_check_hb_timeout()
        t = threading.Thread(target=self._start_check_hb_timeout, args=())
        self.threads.append(t)
        t.setName("check_hb_timeout")
        t.setDaemon(True)
        t.start()

    def _start_httpserver(self):
        #path = /hb_timeout?from='xxxx'&to="xxxx"

        def write_netblock(environ, start_response):
            response_body = []
            query = environ.get("QUERY_STRING", None)
            querys = str2dict(query)
            netblock = querys.get("netblock", None)
            self.set_netblock(netblock)
            return query

        def application(environ, start_response):
            path = environ.get('PATH_INFO')
            if path == '/netblock':
                response_body = write_netblock(environ, start_response)
            else:
                response_body = "unsupport, just support netblock"

            #print("http path %s, environ: %s" % (path, environ))
            status = "200 OK"
            response_headers = [("Content-Length", str(len(response_body)))]
            start_response(status, response_headers)
            return [response_body]

        try:
            httpd = make_server('0.0.0.0', self.agent_port, application)
            httpd.serve_forever()
        except Exception, e:
            print str(e),e.errno
            raise Exception(e.errno, e.strerror)

    def start_httpserver(self):
        t = threading.Thread(target=self._start_httpserver, args=())
        self.threads.append(t)
        t.setName("start_httpserver")
        t.setDaemon(True)
        t.start()

    def run(self):
        self.start_check_hb_timeout()
        self.start_httpserver()
        self.wait_finish()

def usage():
    print ("usage:")
    print (sys.argv[0] + " --start --admin_host <ip||hostname]> --admin_port <port> --agent_port <port>")
    print (sys.argv[0] + " --stop")
    print (sys.argv[0] + " --test --admin_host <ip||hostname]> --admin_port <port> --agent_port <port>")
    exit(1)

def main():
    op = ''
    ext = None
    admin_host = None
    admin_port = 0
    agent_port = 0
    try:
        opts, args = getopt.getopt(
                sys.argv[1:], 
            'h', ['start', 'stop', 'help', 'test', 'admin_host=', 'agent_port=', 'admin_port=']
                )
    except getopt.GetoptError, err:
        print str(err)
        usage()

    for o, a in opts:
        if o in ('--help'):
            usage()
            exit(0)
        elif o == '--start':
            op = o
        elif o == '--stop':
            op = o
        elif o == '--test':
            op = o
        elif o == '--admin_host':
            admin_host = a
        elif o == '--admin_port':
            admin_port = a
        elif o == '--agent_port':
            agent_port = a
        else:
            assert False, 'oops, unhandled option: %s, -h for help' % o
            exit(1)

    config = Config()
    lich_cron = Lich_check_suspend_agent(config, admin_host, admin_port, agent_port)
    if (op == '--start'):
        lich_cron.start()
    elif (op == '--stop'):
        lich_cron.stop()
    elif (op == '--test'):
        lich_cron.run()
    else:
        assert False, 'oops, unhandled option: %s, -h for help' % o
        exit(1)


if __name__ == '__main__':
    if (len(sys.argv) == 1):
        usage()
    else:
        main()
